#pragma once

#include<iostream>
#include<cstdio>
#include<pthread.h>
#include<semaphore.h>
#include<vector>

template<typename T>
class RingQueue
{
public:
    RingQueue(int cap) 
        :_ring_queue(cap),_cap(cap),_producer_index(0),_consumer_index(0)
    {
        sem_init(&_sem_room, 0, 10); //空间资源数目
        sem_init(&_sem_data, 0, 0); //数据资源数目
        pthread_mutex_init(&_mutex_producer, nullptr);
        pthread_mutex_init(&_mutex_consumer, nullptr);
    }
    
    void EnQueue(T& data)
    {
        //信号量PV操作维护生产者消费者同步关系,间接维护互斥关系
        sem_wait(&_sem_room); //申请空间资源,队列满,等待消费者消费,释放空间
        pthread_mutex_lock(&_mutex_producer);

        //生产数据
        _ring_queue[_producer_index++] = data;
        _producer_index %= _cap; //"环形"

        pthread_mutex_unlock(&_mutex_producer);
        sem_post(&_sem_data); //释放数据资源
    }

    void PopQueue(T* data)
    {
        sem_wait(&_sem_data);//申请数据资源,队列空,等待生产者生产
        pthread_mutex_lock(&_mutex_consumer);

        //消费数据
        *data = _ring_queue[_consumer_index++];
        _consumer_index %= _cap; //"环形"

        pthread_mutex_unlock(&_mutex_consumer);
        sem_post(&_sem_room); //释放空间资源
    }

    ~RingQueue()
    {
        sem_destroy(&_sem_room);
        sem_destroy(&_sem_data);
        pthread_mutex_destroy(&_mutex_producer);
        pthread_mutex_destroy(&_mutex_consumer);
    }

private:
    //1.环形队列
    std::vector<T> _ring_queue; //需要提前给数组开辟空间
    int _cap; //最大容量

    //2.生产者和消费者下标,用来定位对应资源
    int _producer_index;
    int _consumer_index;

    //3.信号量
    sem_t _sem_room;
    sem_t _sem_data;

    //4.互斥锁,用来维护生产者之间/消费者之间的互斥关心
    pthread_mutex_t _mutex_producer;
    pthread_mutex_t _mutex_consumer;
};

